#ifndef __M_DB_H__
#define __M_DB_H__

#pragma once

#include "util.hpp"
#include <mutex>

//password('%s')对输入的密码进行加密后存入数据库表中
#define INSERT_USER "insert user values(null, '%s', password('%s'), 1000, 0, 0);"
//以用户名和密码共同作为过滤条件进行用户信息的查询 查询到唯一的用户信息则登录成功
#define LOGIN_USER "select id, score, total_count, win_count from user where username='%s' and password=password('%s');"
#define SELECT_BY_NAME "select id, score, total_count, win_count from user where username='%s';"
#define SELECT_BY_ID "select username, score, total_count, win_count from user where id=%d;"
#define USER_WIN "update user set score=score+30, total_count=total_count+1, win_count=win_count+1 where id=%d;"
#define USER_LOSE "update user set score=score-30, total_count=total_count+1 where id=%d;"

class user_table
{
public:
    user_table(const std::string& host, const std::string& user, const std::string& pwd, const std::string& db, uint16_t port = 3306)
    {
        _mysql = mysql_util::mysql_create(host.c_str(), user.c_str(), pwd.c_str(), db.c_str(), port);
        if (_mysql == nullptr)
            exit(-1);
    }

    ~user_table()
    {
        mysql_util::mysql_destroy(_mysql);
        _mysql = nullptr;
    }

    bool insert(Json::Value& user)//注册新增用户
    {
        //通过设置username unique key 无需在插入时判断用户名是否已经存在
        // Json::Value val;
        // if (select_by_name(user["username"].asCString(), val))
        // {
        //     LOG(ERR, "user:%s already exists", user["username"].asCString());
        //     return false;
        // }
        if (user["username"].isNull() || user["password"].isNull())//server形成响应时还会再判断一次 这里可以不用判断
        {
            LOG(ERR, "please input username or password");
            return false;
        }
        
        char sql[4096];
        sprintf(sql, INSERT_USER, user["username"].asCString(), user["password"].asCString());
        if (!mysql_util::mysql_execute(_mysql, sql))
        {
            LOG(ERR, "insert user failed");
            return false;
        }
        return true;
    }

    bool login(Json::Value& user)//登录验证
    {
        if (user["username"].isNull() || user["password"].isNull())
        {
            LOG(ERR, "please input username or password");
            return false;
        }

        char sql[4096];
        sprintf(sql, LOGIN_USER, user["username"].asCString(), user["password"].asCString());
        
        MYSQL_RES* res;
        {
            std::unique_lock<std::mutex> lock(_mutex);//在将结果保存到本地(res)之前其他线程不能进来执行sql语句
            if (!mysql_util::mysql_execute(_mysql, sql))
            {
                LOG(ERR, "user login failed");
                return false;
            }

            res = mysql_store_result(_mysql);
            // if (res == nullptr)
            // {
            //     LOG(ERR, "user info is not correct, please check name and password");
            //     return false;
            // }
        }

        int row_num = mysql_num_rows(res);
        if (row_num == 0)
        {
            LOG(ERR, "user info is not correct, please check name and password");
            return false;
        }
        if (row_num > 1)
        {
            LOG(ERR, "user info is not unique");
            return false;
        }

        MYSQL_ROW row = mysql_fetch_row(res);
        user["id"] = (Json::UInt64)std::stol(row[0]);//Json::Value不支持long类型数据赋值转换 需要使用Json提供的长整型类型
        user["score"] = (Json::UInt64)std::stol(row[1]);
        user["total_count"] = std::stoi(row[2]);
        user["win_count"] = std::stoi(row[3]);

        mysql_free_result(res);
        return true;
    }

    bool select_by_name(const std::string& name, Json::Value& user)
    {
        char sql[4096];
        sprintf(sql, SELECT_BY_NAME, name.c_str());

        MYSQL_RES* res;
        {
            std::unique_lock<std::mutex> lock(_mutex);
            if (!mysql_util::mysql_execute(_mysql, sql))
            {
                LOG(ERR, "select user by name failed");
                return false;
            }

            res = mysql_store_result(_mysql);
            // if (res == nullptr)
            // {
            //     LOG(ERR, "user:%s not exists", name.c_str());
            //     return false;
            // }
        }

        int row_num = mysql_num_rows(res);
        if (row_num == 0)
        {
            LOG(ERR, "user:%s not exists", name.c_str());
            return false;
        }
        if (row_num > 1)
        {
            LOG(ERR, "user info is not unique");
            return false;
        }

        MYSQL_ROW row = mysql_fetch_row(res);
        user["id"] = (Json::UInt64)std::stol(row[0]);
        user["username"] = name;
        user["score"] = (Json::UInt64)std::stol(row[1]);
        user["total_count"] = std::stoi(row[2]);
        user["win_count"] = std::stoi(row[3]);

        mysql_free_result(res);
        return true;
    }

    bool select_by_id(uint64_t id, Json::Value& user)
    {
        char sql[4096];
        sprintf(sql, SELECT_BY_ID, id);

        MYSQL_RES* res;
        {
            if (!mysql_util::mysql_execute(_mysql, sql))
            {
                LOG(ERR, "select user by id:%lu failed", id);
                return false;
            }

            res = mysql_store_result(_mysql);
            // if (res == nullptr)
            // {
            //     LOG(ERR, "user id:%d not exists", id);
            //     return false;
            // }
        }

        int row_num = mysql_num_rows(res);
        if (row_num == 0)
        {
            LOG(ERR, "user id:%d not exists", id);
            return false;
        }
        if (row_num > 1)
        {
            LOG(ERR, "user info is not unique");
            return false;
        }

        MYSQL_ROW row = mysql_fetch_row(res);
        user["id"] = (Json::UInt64)id;
        user["username"] = row[0];
        user["score"] = (Json::UInt64)std::stol(row[1]);
        user["total_count"] = std::stoi(row[2]);
        user["win_count"] = std::stoi(row[3]);

        mysql_free_result(res);
        return true;
    }

    bool win(uint64_t id)//用户胜利 分数增加 总场次增加 胜场增加
    {
        char sql[4096];
        sprintf(sql, USER_WIN, id);
        if (!mysql_util::mysql_execute(_mysql, sql))
        {
            LOG(ERR, "update win user info failed");
            return false;
        }
        return true;
    }

    bool lose(uint64_t id)//用户失败 分数减少 总场次增加
    {
        char sql[4096];
        sprintf(sql, USER_LOSE, id);
        if (!mysql_util::mysql_execute(_mysql, sql))
        {
            LOG(ERR, "update lose user info failed");
            return false;
        }
        return true;
    }

private:
    MYSQL* _mysql;
    std::mutex _mutex;//保证mysql不同接口的线程安全
};

#endif